#include <stdio.h>
#include <errno.h>
#include <stdbool.h>
#include <stdlib.h>
#include <strings.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/select.h>
#include <fcntl.h>
#include <termios.h>
#include <pthread.h>

#include <dirent.h>

//gec6818串口
#define 	USART1		"/dev/ttySAC1"
#define 	USART2		"/dev/ttySAC2"
#define 	USART3		"/dev/ttySAC3"

unsigned char WBuf[128], RBuf[128];//写入与读取RFID数据的缓冲区
struct timeval timeout;//时间结构体
volatile unsigned int cardid ;//IC卡卡号变量
unsigned char KEY_BUF[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};//验证码
unsigned char DataWriteBuf[16];//写入IC卡的数据
unsigned char DataReadBuf[16];//从IC卡中读取的数据

//初始化串口设置---波特率9600，无数据位8位，无奇偶校验位，停止位1位
void init_tty(int fd)
{
	struct termios new_cfg, old_cfg;
	if(tcgetattr(fd, &old_cfg) != 0)
	{
		perror("tcgetattr() failed");
		exit(0);
	}

	bzero(&new_cfg, sizeof(new_cfg));

	new_cfg = old_cfg;
	cfmakeraw(&new_cfg);

	cfsetispeed(&new_cfg, B9600);
	cfsetospeed(&new_cfg, B9600);

	new_cfg.c_cflag |= CLOCAL | CREAD;

	new_cfg.c_cflag &= ~CSIZE;
	new_cfg.c_cflag |= CS8;

	new_cfg.c_cflag &= ~PARENB;
	new_cfg.c_cflag &= ~CSTOPB;

	new_cfg.c_cc[VTIME] = 0;
	new_cfg.c_cc[VMIN]  = 1;
	tcflush(fd, TCIFLUSH);

	if(tcsetattr(fd, TCSANOW, &new_cfg) != 0)
	{
		perror("tcsetattr() failed");
		exit(0);
	}
}

/*计算校验和*/
unsigned char CalBCC(unsigned char *buf, int n)
{
	int i;
	unsigned char bcc=0;
	for(i = 0; i < n; i++)
	{
		bcc ^= *(buf+i);
	}
	return (~bcc);
}

int Picc_request(int fd)//发送A命令----请求IC卡
{
	int  ret;
	fd_set rdfd;
	
	//清空缓冲区
	memset(WBuf, 0, 128); 
	memset(RBuf,0,128);
	
	WBuf[0] = 0x07;	//帧长= 7 Byte
	WBuf[1] = 0x02;	//包号= 0 , 命令类型
	WBuf[2] = 0x41;	//命令= 'A'
	WBuf[3] = 0x01;	//信息长度
	WBuf[4] = 0x52;	//请求模式:  ALL=0x52
	WBuf[5] = CalBCC(WBuf, WBuf[0]-2);		//校验和
	WBuf[6] = 0x03; 	//结束标志
	
	while(1)
	{
		FD_ZERO(&rdfd);
		FD_SET(fd,&rdfd);
		tcflush (fd, TCIFLUSH); 

		//设置超时时间为2秒
		timeout.tv_sec = 2;
		timeout.tv_usec = 0;
		
		write(fd, WBuf, 7);
		usleep(10*1000);//10ms延时
	
		ret = select(fd + 1,&rdfd, NULL,NULL,&timeout);  //监测文件描述符的变化
		switch(ret)
		{
			case -1:
				perror("request select error\n");
				break;
				
			case  0:
				printf("Request timed out.\n");
				break;
				
			default:
				usleep(10000);
				ret = read(fd, RBuf, 8);
				if(ret < 0)
				{
					printf("len = %d, %m\n", ret, errno);
					break;
				}
				
				//printf("RBuf[2]:%x\n", RBuf[2]);
				if(RBuf[2] == 0x00)	 	//应答帧状态部分为0 则请求成功
				{
					return 0;
				}
				break;
		}
		
		usleep(100000);
		
	}
	
	return -1;
}

int Picc_getID(int fd)/*防碰撞，获取范围内最大ID*/
{
	int ret=1;
	fd_set rdfd;
	
	memset(WBuf, 0, 128);
	memset(RBuf,1,128);
	
	WBuf[0] = 0x08;	//帧长= 8 Byte
	WBuf[1] = 0x02;	//包号= 0 , 命令类型= 0x01
	WBuf[2] = 0x42;	//命令= 'B'
	WBuf[3] = 0x02;	//信息长度= 2
	WBuf[4] = 0x93;	//防碰撞0x93
	WBuf[5] = 0x00;	//位计数0
	WBuf[6] = CalBCC(WBuf, WBuf[0]-2);		//校验和
	WBuf[7] = 0x03; 	//结束标志


	tcflush (fd, TCIFLUSH);
	FD_ZERO(&rdfd);
	FD_SET(fd,&rdfd);
	
	//设置超时时间为2秒
	timeout.tv_sec = 2;
	timeout.tv_usec = 0;
	
	write(fd, WBuf, 8);
	usleep(10*1000);//10ms延时
	ret = select(fd + 1,&rdfd, NULL,NULL,&timeout);
	switch(ret)
	{
		case -1:
			perror("getID select error\n");
			break;
		case  0:
			perror("getID Timeout");
			break;
			
		default:
			usleep(10000);
			ret = read(fd, RBuf, 10);
			if (ret < 0)
			{
				printf("ret = %d\n", ret);
				break;
			}
			//printf("RBuf[2]=%x\n",RBuf[2]);
			if (RBuf[2] == 0x00) //应答帧状态部分为0 则获取ID 成功
			{
				cardid = (RBuf[4]) | (RBuf[5]<<8) | (RBuf[6]<<16) | RBuf[7]<<24;
				return 0;
			}
	}

	
	
	return -1;
}


int Picc_select(int fd)//选择IC卡
{
	unsigned char WBuf[128], RBuf[128];
	int  ret, i;
	fd_set rdfd;
	static struct timeval timeout;

	memset(WBuf, 0, 128);
	memset(RBuf,0,128);
	WBuf[0] = 0x0B;	//帧长= 7 Byte
	WBuf[1] = 0x02;	//包号= 0 , 命令类型= 0x02
	WBuf[2] = 'C';	//命令= 'C
	WBuf[3] = 0x05;	//信息长度= 5
	WBuf[4] = 0x93;	//请求模式:  ALL=0x52
   
	//*********选择IC卡**********
	WBuf[5] = (cardid) & 0xff;//取24~31位
	WBuf[6] = (cardid>>8)  & 0xff;//取16~23位
	WBuf[7] = (cardid>>16) & 0xff;//取8~15位
	WBuf[8] = cardid>>24 & 0xff;//取0~7位
	
	WBuf[9] = CalBCC(WBuf, WBuf[0]-2);		//校验和
	WBuf[10] = 0x03; 	//结束标志

	timeout.tv_sec = 300;
	timeout.tv_usec = 0;
	FD_ZERO(&rdfd);
	FD_SET(fd,&rdfd);
	
	write(fd, WBuf, WBuf[0]);
	usleep(10*1000);//10ms延时
	ret = select(fd + 1,&rdfd, NULL,NULL,&timeout);
	switch(ret)
	{
		case -1:
			perror("PiccSelect error\n");
			break;
		case  0:
			printf("PiccSelect timed out.\n");
			break;
		default:
			ret = read(fd, RBuf, 7);
			if (ret < 0)
			{
				printf("PiccSelect  ret = %d, %m\n", ret, errno);
				break;
			}
			//printf("RBuf[2]=%x\n",RBuf[2]);
			if (RBuf[2] == 0x00)	 	//应答帧状态部分为0 则请求成功
			{
			    printf("PiccSelect success\n");
				return 0;
			}
			break;
	}
	return -1;
}

int Picc_Key(int fd,int sector,unsigned char KeyAB)//验证密钥
{
	unsigned char WBuf[128], RBuf[128];
	int  ret, i;
	fd_set rdfd;

	memset(WBuf, 0, 128);
	memset(RBuf,0,128);
	
	WBuf[0] = 0x12;	//帧长= 17 Byte
	WBuf[1] = 0x02;	//包号= 0 , 命令类型= 0x02
	WBuf[2] = 'F';	//命令= 'F'
	WBuf[3] = 0x0C;	//信息长度= 0x0c
	WBuf[4] = KeyAB;
	
	//*********选择IC卡**********
	WBuf[5] = (cardid) & 0xff;//取24~31位
	WBuf[6] = (cardid>>8)  & 0xff;//取16~23位
	WBuf[7] = (cardid>>16) & 0xff;//取8~15位
	WBuf[8] = (cardid>>24) & 0xff;//取0~7位
	//*********验证码*************
	memcpy(&WBuf[9],KEY_BUF,6);//buf[9]~buf[14]都是0xFF
	
	WBuf[15] = sector;   //读写哪个区
	WBuf[16] = CalBCC(WBuf, WBuf[0]-2);		//校验和
	WBuf[17] = 0x03; 	//结束标志

	timeout.tv_sec = 300;
	timeout.tv_usec = 0;
	FD_ZERO(&rdfd);
	FD_SET(fd,&rdfd);

	write(fd, WBuf, 18);
	usleep(10*1000);//10ms延时
	ret = select(fd + 1,&rdfd, NULL,NULL,&timeout);
	switch(ret)
	{
		case -1:
			perror("Picc_Key error\n");
			break;
		case  0:
			printf("Picc_Key timed out.\n");
			break;
		default:
			ret = read(fd, RBuf, 7);
			if (ret < 0)
			{
				printf("Picc_Key  ret = %d, %m\n", ret, errno);
				break;
			}
				
			//printf("RBuf[2]=%x\n",RBuf[2]);
			if (RBuf[2] == 0x00)	 	//应答帧状态部分为0 则请求成功
			{
				//printf("Picc_Key success\n");
				return 0;
			}
			break;
	}
	return -1;
}

int PiccWrite(int fd,unsigned char sector,char *str,int num )//向扇区中写入数据
{
	unsigned char WBuf[128], RBuf[128];
	int  ret, i;
	fd_set rdfd;
	static struct timeval timeout;

	memset(WBuf, 0, 128);
	memset(RBuf,0,128);
	memset(DataWriteBuf, 0, 16);
	
	//向扇区中写入的数据----预定剩余金额
	//注意此时为unsigned char类型变量，取值范围0~255
	memcpy(DataWriteBuf,str,num);
	
	//指令
	WBuf[0] = 0x17;	//帧长= 23 Byte
	WBuf[1] = 0x02;	//包号= 0 , 命令类型= 0x02
	WBuf[2] = 'H';	//命令= 'H
	WBuf[3] = 0X11;	//信息长度= 17
	WBuf[4] = sector;   //写哪个扇区
	
    memcpy(&WBuf[5],DataWriteBuf,16);
	
	WBuf[21] = CalBCC(WBuf, WBuf[0]-2);	//校验和
	WBuf[22] = 0x03; 	//结束标志

    timeout.tv_sec = 300;
    timeout.tv_usec = 0;
	FD_ZERO(&rdfd);
	FD_SET(fd,&rdfd);
        
	write(fd, WBuf, 23);
	ret = select(fd + 1,&rdfd, NULL,NULL,&timeout);
	switch(ret)
	{
		case -1:
			perror("PiccWrite error\n");
			break;
		case  0:
			printf("PiccWrite timed out.\n");
			break;
		default:
			ret = read(fd, RBuf, 7);
			if (ret < 0)
			{
				printf("PiccWrite  ret = %d, %m\n", ret, errno);
				break;
			}
			
			if (RBuf[2] == 0x00)	 	//应答帧状态部分为0 则请求成功
			{
			    printf("PiccWrite card success\n");
				return 0;
			}
			break;
	}
	return -1;
}

int PiccRead(int fd,unsigned char sector)
{
	unsigned char WBuf[128], RBuf[128];
	int  ret, i ;
	fd_set rdfd;
	static struct timeval timeout;

	memset(WBuf, 0, 128);
	memset(RBuf,0,128);
	WBuf[0] = 0x07;	//帧长= 7 Byte
	WBuf[1] = 0x02;	//包号= 0 , 命令类型= 0x02
	WBuf[2] = 'G';	//命令= 'g
	WBuf[3] = 0x01;	//信息长度= 1
	WBuf[4] = sector;	//读哪个扇区
	WBuf[5] = CalBCC(WBuf, WBuf[0]-2);		//校验和
	WBuf[6] = 0x03; 	//结束标志

    timeout.tv_sec = 300;
    timeout.tv_usec = 0;
	FD_ZERO(&rdfd);
	FD_SET(fd,&rdfd);

	write(fd, WBuf, WBuf[0]);
	ret = select(fd + 1,&rdfd, NULL,NULL,&timeout);
	switch(ret)
	{
		case -1:
			perror("PiccRead error\n");
			break;
		case  0:
			printf("PiccRead timed out.\n");
			break;
		default:
			ret = read(fd, RBuf, 23);
			if (ret < 0)
			{
				printf("PiccRead  ret = %d, %m\n", ret, errno);
				break;
			}
			//printf("PiccR RBuf[2]=%x\n",RBuf[2]);
			if (RBuf[2] == 0x00)	 	//应答帧状态部分为0 则请求成功
			{
			        usleep(10*1000);
			        //read(fd,&RBuf[ret],6);
			        memcpy(DataReadBuf,&RBuf[4],16); //读取到该扇区的数据
			        // for(i=0;i<16;i++)
			        // {
			        //     printf("PiccRead RBuf[%d]=>%x\n",i,DataReadBuf[i]);
			        // }
				return 0;
			}
			break;
	}
	return -1;
}

int main(void)
{
	int fd_serial = open(USART1, O_RDWR|O_NOCTTY);//打开串口1设备文件
	if(fd_serial == -1)
	{
		perror("open USART1 failed\n");
		exit(0);
	}

	while(Picc_request(fd_serial) == -1);//请求IC卡
	
	if(Picc_getID(fd_serial) == 0)//获取卡号
		printf("cardid=%X\n",cardid);
	
	if(Picc_select(fd_serial) == 0)//选中IC卡
		printf("select success\n");

	unsigned int sector=1;//选择扇区1
	if(Picc_Key(fd_serial,sector,0x60)==0)
		printf("Picc_Key 1  success \n");
	
/*	//如果0x60密钥不成功，需要重新从请求IC卡开始
	else
	{
		printf("***************\n");
		while(Picc_request(fd_serial) == -1);
	
		if(Picc_getID(fd_serial) == 0)
			printf("cardid=%X\n",cardid);//注意卡号为十六进制
		
		if(Picc_select(fd_serial) == 0)
			printf("select success\n");//注意卡号为十六进制
		
		if(Picc_Key(fd_serial,sector,0x61)==0)
			printf("Picc_Key 2  success \n");
	}
*/
	
	PiccRead(fd_serial , sector);
	
	printf("==%s==\n",DataReadBuf);
	
	return 0;
}
